/*****************************************************************************
*    Copyright (C)2003 Ali Corporation. All Rights Reserved.
*
*    File:    nim_max2118.c
*
*    Description:    This file contains MAX2118 basic function in LLD.
*    History:
*           Date            Athor        Version          Reason
*	    ============	=============	=========	=================
*	1.  Jan.05.2004		David Wang		 Ver 0.1	Create file.
*	2.  Jul.25.2005     Berg Xing        Ver 0.2    Different/ Single end select
*	3.  08/31/2005	Zhu Jun		Ver 1.1		Unify Tuner's configuration API Functions
*
*****************************************************************************/

#include <sys_config.h>
#include <retcode.h>
#include <types.h>

#include <api/libc/printf.h>
#include <api/libc/string.h>
#include <bus/i2c/i2c.h>
#include <osal/osal_task.h>
#include "nim_max2118.h"

struct QPSK_TUNER_CONFIG_EXT * stMAX2118_dev_id[MAX_TUNER_SUPPORT_NUM] = {NULL};
static UINT32 stMAX2118_tuner_cnt = 0;
/*****************************************************************************
* INT32 nim_max2118_init(UINT32 para, struct QPSK_TUNER_CONFIG_EXT * ptrTuner_Config)
*
* Tuner MAX2118 Initialization
*
* Arguments:
*  Parameter1: UINT32 para		: Input Parameter
*  Parameter2: struct QPSK_TUNER_CONFIG_EXT * ptrTuner_Config		: pointer for Tuner configuration structure
*
* Return Value: INT32			: Result
*****************************************************************************/
INT32 nim_max2118_init(UINT32* tuner_id, struct QPSK_TUNER_CONFIG_EXT * ptrTuner_Config)
{
	struct QPSK_TUNER_CONFIG_EXT * stMAX2118_ptr = NULL;
	/* check Tuner Configuration structure is available or not */
	if (ptrTuner_Config == NULL||stMAX2118_tuner_cnt>=MAX_TUNER_SUPPORT_NUM)
		return ERR_FAILUE;
	stMAX2118_ptr = (struct QPSK_TUNER_CONFIG_EXT *)MALLOC(sizeof(struct QPSK_TUNER_CONFIG_EXT));
	if(!stMAX2118_ptr)
		return ERR_FAILUE;
	stMAX2118_dev_id[stMAX2118_tuner_cnt] = stMAX2118_ptr;
	MEMCPY(stMAX2118_ptr, ptrTuner_Config, sizeof(struct QPSK_TUNER_CONFIG_EXT));
	*tuner_id = stMAX2118_tuner_cnt;
	stMAX2118_tuner_cnt++;
/*	soc_printf("%s: sizeof QPSK_TUNER_CONFIG_EXT = %d\n", __FUNCTION__, sizeof(struct QPSK_TUNER_CONFIG_EXT));
	soc_printf("Base Address: 0x%x\n",stMAX2118_Config.cTuner_Base_Addr);
	soc_printf("Tuner Crystal: %dKHz\n",stMAX2118_Config.wTuner_Crystal);
	soc_printf("Output Mode: %d (1 -- Signle End; 0 -- Differential)\n",stMAX2118_Config.cTuner_Out_S_D_Sel);
*/
	return SUCCESS;
}

/*****************************************************************************
* INT32 max2118_read(UINT8 reg,UINT8* buff,UINT8 len)
*
* Tuner read register from MAX2118
*
* Arguments:
*  Parameter1: UINT8 reg		: Register offset of MAX2118
*  Parameter2: UINT8* buff		: Read data buffer
*  Parameter3: UINT8 len		: Read length
*
* Return Value: INT32			: Result
*****************************************************************************/
INT32 max2118_read(UINT32 i2c_type_id, UINT8 base_addr, UINT8 reg,UINT8* buff,UINT8 len)
{
	buff[0] = reg;
	return i2c_write_read(i2c_type_id, base_addr, buff, 1, len);
}

/*****************************************************************************
* INT32 max2118_write(UINT8 reg,UINT8* buff,UINT8 len)
*
* Tuner write register from MAX2118
*
* Arguments:
*  Parameter1: UINT8 reg		: Register offset of MAX2118
*  Parameter2: UINT8* buff		: write data buffer
*  Parameter3: UINT8 len		: write length
*
* Return Value: INT32			: Result
*****************************************************************************/
INT32 max2118_write(UINT32 i2c_type_id, UINT8 base_addr, UINT8 reg,UINT8* buff,UINT8 len)
{
	UINT8  i, buffer[8];

	if (len > 7)
	{
	    return ERR_FAILUE;
	}
	buffer[0] = reg;
	for (i = 0; i < len; i++)
	{
	    buffer[i + 1] = buff[i];
	}

	return i2c_write(i2c_type_id, base_addr, buffer, len+1);
}

/*****************************************************************************
* int Read2118ADC(INT8 byte5)
*
* Read the ADC value of MAX2118
*
* Arguments:
*  Parameter1: INT8 byte5		: offset of MAX2118 register
*
* Return Value: INT32			: Result
*****************************************************************************/
int Read2118ADC(UINT32 i2c_type_id, UINT8 base_addr, INT8 byte5)
{
	unsigned addr;
	UINT8 byte;
	byte = byte5;

	max2118_write(i2c_type_id, base_addr, 4,&byte,1);
	osal_task_sleep(1);
	byte |= 0xC0;
	max2118_write(i2c_type_id, base_addr, 4,&byte,1);

	osal_task_sleep(1);
	max2118_read(i2c_type_id, base_addr, 0,&byte,1);
	byte=(byte/4)&0x7;
	return byte;
}

/*****************************************************************************
* INT32 nim_max2118_control(UINT32 freq, UINT32 sym)
*
* Tuner write operation
*
* Arguments:
*  Parameter1: UINT32 freq		: Synthesiser programmable divider
*  Parameter2: UINT32 sym			: Baseband Symbol Rate
*  Parameter3: UINT8 cp			: Charge pump current
*
* Return Value: INT32			: Result
*****************************************************************************/
INT32 nim_max2118_control(UINT32 tuner_id, UINT32 freq, UINT32 sym)
{
	UINT8 data[6],VcoNo,byte,tmp,i;
	UINT16 divider;
	UINT32 xx;
	INT32 x,y,j;
	UINT32 vcofrq;
	UINT8 Update = 0;
	UINT8 bRead[2];
	UINT8 bs;
	struct QPSK_TUNER_CONFIG_EXT * stMAX2118_ptr = NULL;
	/* check Tuner Configuration structure is available or not */
	if (tuner_id>=stMAX2118_tuner_cnt||tuner_id>=MAX_TUNER_SUPPORT_NUM)
		return ERR_FAILUE;
	stMAX2118_ptr = stMAX2118_dev_id[tuner_id];
		
	bs = (sym+100)/200;

	if (stMAX2118_ptr->wTuner_Crystal == 10000)		// 10M Crystal
		data[2]=0x78;
	else if (stMAX2118_ptr->wTuner_Crystal == 13500)		// 13M Crystal
		data[2]=0x98;
	else
		data[2]=0x58;

	tmp = (data[2]>>5)&0x07;
	byte = 0x01<<(tmp+1);
	//soc_printf("%s: step = %d\n", __FUNCTION__,  byte);
	if (stMAX2118_ptr->wTuner_Crystal == 13500)
        divider = freq*1000/422;
    else
	divider = freq * 1000 / (stMAX2118_ptr->wTuner_Crystal / byte);


	data[0]=(char) ((divider>>8)&0x7f);
	data[1]=(char) (divider&0xff);

// ZCY: now bs is just Rs*5
{
	float Rs, BW;
	Rs = (float)bs / 5;
	if (Rs==0)
		Rs = 45.0;
	BW = Rs*1.35 / 2;               // rolloff is 35%
	BW = BW*1.3;                    // 0.1dB to 3dB need about 30% transition band
	if (Rs<6.5)  BW = BW + 6.0;     // add 3M when Rs<5M
	BW = BW + 2.0;                  // add 2M for LNB frequency shifting
	BW = BW*1.08;                   // add 8% margin since fc is not very accurate
	if (BW< 4)   BW =  4;           // Maxim LPF can be tuned form 4M to 33M, step is 0.145M
	if (BW>35)   BW = 35;           // when more than 35M, filter character has some distortion in my test
	if (BW < (4+127*0.145))         // fc = fx/M * (4+0.145*FDAC)
	{
		if (stMAX2118_ptr->wTuner_Crystal == 10000)		// 10M Crystal
		{
			byte = 0xa;
			xx = (UINT32) ((BW  -4)/0.145);
		}
		else if (stMAX2118_ptr->wTuner_Crystal == 13500)		// 13M Crystal
		{
			byte = 0xd;
			xx = (UINT32) (((BW/1.03846  -4)/0.145)+0.5);
		}
		else	//  4M Crystal
		{
			byte = 0x4;
			xx = (UINT32) ((BW  -4)/0.145);
		}
	}
	else
	{
		if (stMAX2118_ptr->wTuner_Crystal == 10000)		// 10M Crystal
		{
			byte = 0x5;
			xx = (UINT32) ((BW/2-4)/0.145);
		}
		else if (stMAX2118_ptr->wTuner_Crystal == 13500)		// 13M Crystal
		{
			byte = 0x6;
			xx = (UINT32) (((BW/2.25-4)/0.145) +0.5);
		}
		else	//  4M Crystal
		{
			byte = 0x2;
			xx = (UINT32) ((BW/2-4)/0.145);
		}
	}
	if (xx>127)  xx=127;
	data[3] = xx;
	//M3327_DEBUG (" Maxim Tuner Parameter:\n");
	//M3327_DEBUG ("    Rs = %d\n", (UINT32)(Rs*1000));
	//M3327_DEBUG ("    BW = %d\n", (UINT32)(BW*1000));
	//M3327_DEBUG ("    M = %d\n", byte );
	//M3327_DEBUG ("    FDAC = %d\n", xx );
}


	//if we use cha feng , so set DL = 0
	//if we use dan duan , so set DL = 1

	if (stMAX2118_ptr->cTuner_Out_S_D_Sel == 0)
		data[4]=0x40|0x00|byte; //set DL = 0
	else
		data[4]=0x40|0x20|byte;  //set DL=1

	data[5]=0x0a;
	if(freq<1125)
	{
		vcofrq=freq*4;
	}
	else
	{
		vcofrq=freq*2;
		data[0]=data[0]|0x80;
	}
	if (vcofrq<2433)
		VcoNo=0;
	else if(vcofrq<2711)
		VcoNo=1;
	else if(vcofrq<3025)
		VcoNo=2;
	else if(vcofrq<3341)
		VcoNo=3;
	else if(vcofrq<3727)
		VcoNo=4;
	else if(vcofrq<4143)
		VcoNo=5;
	else if( vcofrq<4493)
		VcoNo=6;
	else
		VcoNo=7;

	data[2]|= VcoNo;

	for(i=0;i<6;i++)
	{
		if(max2118_write(stMAX2118_ptr->i2c_type_id, stMAX2118_ptr->cTuner_Base_Addr, i,data+i,1) != SUCCESS)
		{
			PRINTF("nim_max2118_control:I2C Write failed\n");
			return !SUCCESS;
		}
	}
	osal_task_sleep(50);

	byte = Read2118ADC(stMAX2118_ptr->i2c_type_id, stMAX2118_ptr->cTuner_Base_Addr, data[4]);
	//PRINTF(" Read2118ADC is  %d \n", byte );
	if(byte==0)
	{
		if(VcoNo>0)
		{
			VcoNo -= 1;
			Update=1;
		}
	}
	if(byte==7)
	{
		if(VcoNo<7)
		{
			VcoNo += 1;
			Update=1;
		}
	}
	if(Update)
	{
		data[2] = (data[2]&0xF8)|VcoNo;
		max2118_write(stMAX2118_ptr->i2c_type_id, stMAX2118_ptr->cTuner_Base_Addr, 2,data+2,1);
		osal_task_sleep(50);
		byte = Read2118ADC(stMAX2118_ptr->i2c_type_id, stMAX2118_ptr->cTuner_Base_Addr, data[4]);
	}
	if(byte<= 4)
	{
		//if(x==4 || x==3)
		//	tmp=2;
		//else
		//	tmp=1;
		tmp = 3;
		data[2] = data[2]|(tmp<<3)|VcoNo;
		max2118_write(stMAX2118_ptr->i2c_type_id, stMAX2118_ptr->cTuner_Base_Addr, 2,data+2,1);
	}
	return SUCCESS;
}

/*****************************************************************************
* INT32 nim_max2118_status(UINT8 *lock)
*
* Tuner read operation
*
* Arguments:
*  Parameter1: UINT8 *lock		: Phase lock status
*
* Return Value: INT32			: Result
*****************************************************************************/
INT32 nim_max2118_status(UINT32 tuner_id, UINT8 *lock)
{
	if (tuner_id>=stMAX2118_tuner_cnt||tuner_id>=MAX_TUNER_SUPPORT_NUM)
	{
		*lock = 0;
		return ERR_FAILUE;
	}
	/* Because MAX2118 doesn't has this flag,return 1 directly */
	*lock = 1;
	return SUCCESS;
}

